package home.projects.misc;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class ConcurrentRegexTest {

   public static void main(final String[] args) {
      long now = System.currentTimeMillis();
      CountDownLatch latch = new CountDownLatch(3);
      
      LineReader lineReader = new LineReader(new File("c:/a.txt"), latch);
      PatternExecutor patternExecutor = new PatternExecutor(lineReader, latch);
      LineWriter lineWriter = new LineWriter(new File("c:/b.txt"), patternExecutor, latch);
      
      Executor cachedExecutor = Executors.newCachedThreadPool();
      cachedExecutor.execute(lineReader);
      cachedExecutor.execute(patternExecutor);
      cachedExecutor.execute(lineWriter);
      
      System.out.println(System.currentTimeMillis() - now);
      try {
         latch.await();
      } catch (InterruptedException e) {
         e.printStackTrace();
      }

   }
   
}

class LineReader implements Runnable {
   
   private boolean done;
   
   private final CountDownLatch countDownLatch;
   
   private final File inFile;
   
   private final ConcurrentLinkedQueue<String> inLines;
   
   public LineReader(File inFile, CountDownLatch countDownLatch) {
      this.inFile = inFile;
      this.countDownLatch = countDownLatch;
      this.inLines = new ConcurrentLinkedQueue<String>();
   }
   
   public boolean isDone() {
      return done;
   }

   public void run() {
      Scanner in = null;
      try {
         in = new Scanner(inFile);
         while(in.hasNext()) {
            inLines.add(in.next());
         }
      } catch(IOException ioe) {
         throw new RuntimeException(ioe);
      } finally {
         if(in != null) {
            in.close();
            if(in.ioException() != null) {
               in.ioException().printStackTrace();
            }
         }
      }
      
      countDownLatch.countDown();
      done = true;
   }
   
   public ConcurrentLinkedQueue<String> getLines() {
      return inLines;
   }

}

class PatternExecutor implements Runnable {

   private final CountDownLatch countDownLatch;
   
   private boolean done;
   
   private final ConcurrentLinkedQueue<String> processedLines;
   
   private final LineReader lineReader;
   
   public PatternExecutor(LineReader lineReader, CountDownLatch countDownLatch) {
      this.lineReader = lineReader;
      this.countDownLatch = countDownLatch;
      this.processedLines = new ConcurrentLinkedQueue<String>();
   }

   public boolean isDone() {
      return done;
   }
   
   public void run() {
      ConcurrentLinkedQueue<String> inLines = lineReader.getLines();
      while(!lineReader.isDone()) {
         String s = inLines.poll();
         if(s == null)  continue;
         
         // Busy loop for processing
         for(int i = 0; i < 10000; ++i) {}

         processedLines.add(s + Math.random());
      }
      
      countDownLatch.countDown();
      done = true;
   }
   
   public ConcurrentLinkedQueue<String> getProcessedLines() {
      return processedLines;
   }
   
}

class LineWriter implements Runnable {

   private final CountDownLatch countDownLatch;
   
   private final PatternExecutor patternExecutor;
   
   private final File outFile;
   
   private String lineSeparator;
   
   public LineWriter(File outFile, PatternExecutor patternExecutor, CountDownLatch countDownLatch) {
      this.outFile = outFile;
      this.patternExecutor = patternExecutor;
      this.countDownLatch = countDownLatch;
      this.lineSeparator = System.getProperty("line.separator");
   }
   
   public void run() {
      PrintWriter out = null;
      String s = null;
      try {
         out = new PrintWriter(outFile);
         ConcurrentLinkedQueue<String> processedLines = patternExecutor.getProcessedLines();
         while(!patternExecutor.isDone()) {
            s = processedLines.poll();
            if(s == null)  continue;
            out.write(s);
            out.write(lineSeparator);
         }
      } catch(IOException ioe) {
         throw new RuntimeException(ioe);
      } finally {
         if(out != null) {
            out.close();
            if(out.checkError()) {
               System.out.println("Prblem when closing out stream!");
            }
         }
      }

      countDownLatch.countDown();
   }
   
}